package com.turbo.cloud.starter.annotations.unrepeatrequest;

import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson2.JSON;
import com.turbo.cloud.core.security.utils.SecurityUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author zhangluning
 * @description 防止重复请求
 */
@Aspect
@Component
@Slf4j
public class UnRepeatRequestAspect {
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 定义 @Pointcut注解表达式, 通过特定的规则来筛选连接点, 就是Pointcut，选中那几个你想要的方法
     * 在程序中主要体现为书写切入点表达式（通过通配、正则表达式）过滤出特定的一组 JointPoint连接点
     */
    @Pointcut("@annotation(unRepeatRequest)")
    public void pointCutUnRepeatRequest(UnRepeatRequest unRepeatRequest) {

    }

    /**
     * 环绕通知, 围绕着方法执行
     * @param joinPoint
     * @param unRepeatRequest
     */
    @Around("pointCutUnRepeatRequest(unRepeatRequest)")
    public Object around(ProceedingJoinPoint joinPoint, UnRepeatRequest unRepeatRequest) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        String userId = "NoCurrentUser";
        try {
            userId = SecurityUtil.getCurrentUserId();
        } catch (Exception e) {
            log.error("UnRepeatRequest::当前登录用户未获取到");
        }

        String uri = request.getRequestURI();
        String uriStr = DigestUtil.sha256Hex(uri);

        Object requestParam = joinPoint.getArgs();
        String paramStr = "";


        try {
            String paramJson = JSON.toJSONString(requestParam);
            if("[]".equals(paramJson) || "{}".equals(paramJson) ) {
                Map<String, String[]> requestGetParamMap = request.getParameterMap();
                paramJson = JSON.toJSONString(requestGetParamMap);
            }
            paramStr = DigestUtil.sha256Hex(paramJson);
        } catch (Exception e) {
            throw new RuntimeException("UnRepeatRequest防重复请求参数转换异常", e);
        }

        String redisKey = "UnRepeatRequest:" + userId + ":" + uriStr + "_" + paramStr;

        TimeUnit timeUnit = unRepeatRequest.timeunit();
        long timeout = unRepeatRequest.timeout();
        String message = unRepeatRequest.message();

        final ValueOperations<String, String> valueOperations = this.redisTemplate.opsForValue();
        final Boolean result = valueOperations.setIfAbsent(redisKey, "0", timeout, timeUnit);

        if (!result) {
            throw new RuntimeException(message);
        }
        Object obj = joinPoint.proceed();
        return obj;
    }
}
